#include <string>
#include <stdexcept>

#include "sym-info-table.h"
#include "database.h"
#include "autocall-util.h"
#include "string-util.h"

using std::string;
using std::runtime_error;

namespace {

const char *SYMINFO_TABLE_NAME = "syminfo_table";
SymInfoTable *g_syminfo_table_ptr = NULL;
}

SymInfoTable::SymInfoTable()
{
    string sql_stmt_str = format(
                "CREATE TABLE IF NOT EXISTS %s ( "
                "language BLOB NOT NULL,"
                "doc BLOB NOT NULL)",SYMINFO_TABLE_NAME);
    sqlite3 *db_conn_ptr = database_connection();
    exec_sql(db_conn_ptr,sql_stmt_str.c_str());
    return ;
}

SymInfoTable& SymInfoTable::instance()
{
    if (g_syminfo_table_ptr == NULL)
        g_syminfo_table_ptr = new SymInfoTable();
    return *g_syminfo_table_ptr;
}

void SymInfoTable::insert(SymInfo &syminfo)
{
    const string &language = syminfo.language();
    const string &doc = syminfo.doc();

    string sql_stmt_str = format(
        "INSERT INTO %s (language,doc) VALUES ('%s','%s')",
         SYMINFO_TABLE_NAME,
         language.c_str(),
         to_sqlite_str(doc.c_str()).c_str());
    sqlite3 *db_conn_ptr = database_connection();
    exec_sql(db_conn_ptr,sql_stmt_str.c_str());
    syminfo.setId(sqlite3_last_insert_rowid(db_conn_ptr));
    return ;
}

void SymInfoTable::remove(id_t symid)
{
    string sql_stmt_str = format("DELETE FROM %s WHERE ROWID = %lld",
                                 SYMINFO_TABLE_NAME,symid);

    sqlite3 *db_conn_ptr = database_connection();
    exec_sql(db_conn_ptr,sql_stmt_str.c_str());
    return ;
}

void SymInfoTable::get(id_t symid,SymInfo &syminfo)
{
    string language;
    string doc;

    string sql_stmt_str = format("SELECT language,doc FROM %s WHERE rowid = %lld",
                                 SYMINFO_TABLE_NAME,symid);
    sqlite3_stmt *sql_stmt = NULL;
    auto db_conn = database_connection();
    int prepare_ret = sqlite3_prepare_v2(db_conn,sql_stmt_str.c_str(),-1,&sql_stmt,NULL);
    if (prepare_ret != SQLITE_OK) {
        throw runtime_error(format("sql语句 %s prepare 出错;出错原因: %s",
                                   sql_stmt_str.c_str(),
                                   sqlite3_errmsg(db_conn)));
    }
    AUTOCALL_FUNC(destroy_prepare_stmt) {
        if (sql_stmt != NULL)
            sqlite3_finalize(sql_stmt);
    };
    AUTOCALL(destroy_prepare_stmt);

    int step_ret = sqlite3_step(sql_stmt);
    if (step_ret != SQLITE_ROW) {
        throw runtime_error(format("sql语句 %s step 出错;出错原因: %s",
                                   sql_stmt_str.c_str(),
                                   sqlite3_errmsg(db_conn)));
    }
    int language_size = sqlite3_column_bytes(sql_stmt,0);
    int info_size = sqlite3_column_bytes(sql_stmt,1);
    if (language_size <= 0 || info_size <= 0) {
        throw runtime_error(format("获取sql语句 %s 执行结果出错;出错原因: %s",
                                   sql_stmt_str.c_str(),
                                   sqlite3_errmsg(db_conn)));
    }
    const char *language_ptr = static_cast<const char*>(sqlite3_column_blob(sql_stmt,0));
    const char *info_ptr = static_cast<const char*>(sqlite3_column_blob(sql_stmt,1));
    if (language_ptr == 0 || info_ptr == 0) {
        throw runtime_error(format("获取sql语句 %s 执行结果出错;出错原因: %s",
                                   sql_stmt_str.c_str(),
                                   sqlite3_errmsg(db_conn)));
    }
    language.append(language_ptr,language_ptr + language_size);
    doc.append(info_ptr,info_ptr + info_size);

    syminfo.setDoc(doc);
    syminfo.setLanguage(language);
    syminfo.setId(symid);
    return ;
}

void SymInfoTable::modify(id_t symid,const SymInfo &syminfo,unsigned flags)
{
    // 构建 update sql 语句.
    string sql_stmt_str = format("UPDATE %s SET ",SYMINFO_TABLE_NAME);

    /* SET 子句的构建.
     * 关于 SET 字句的构建;若某域要修改,则生成相应的子句,然后追加到 set_sub_stmt_str 之后.
     * 生成子句的格式为",列名 = 值 space".
     * 所以在最后使用 set_sub_stmt_str 之前,应该移除最开始的','.
     */
    string set_sub_stmt_str;
    if (flags & LANGUAGE)
        format(set_sub_stmt_str,",language='%s'",syminfo.language().c_str());
    if (flags & DOC)
        format(set_sub_stmt_str,",doc='%s'",to_sqlite_str(syminfo.doc().c_str()).c_str());
    const char *set_stmt_str = set_sub_stmt_str.data() + 1; // 跳过最开始的','.

    format(sql_stmt_str,"%s WHERE ROWID = %lld",set_stmt_str,symid);

    // 执行 sql 语句.
    sqlite3 *db_conn_ptr = database_connection();
    exec_sql(db_conn_ptr,sql_stmt_str.c_str());
    return ;
}

